home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 10
/
AACD 10.iso
/
AACD
/
Games
/
MAME
/
src
/
machine
/
z80fmly.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-04-23
|
20KB
|
753 lines
/***************************************************************************
Z80 FMLY.C Z80 FAMILY CHIP EMURATOR for MAME Ver.0.1 alpha
Support chip : Z80PIO , Z80CTC
Copyright(C) 1997 Tatsuyuki Satoh.
This version are tested starforce driver.
8/21/97 -- Heavily modified by Aaron Giles to be much more accurate for the MCR games
8/27/97 -- Rewritten a second time by Aaron Giles, with the datasheets in hand
pending:
Z80CTC , Counter mode & Timer with Trigrt start :not support Triger level
***************************************************************************/
#include <stdio.h>
#include "driver.h"
#include "z80fmly.h"
#include "cpu/z80/z80.h"
typedef struct
{
int vector; /* interrupt vector */
int clock; /* system clock */
double invclock16; /* 16/system clock */
double invclock256; /* 256/system clock */
void (*intr)(int which); /* interrupt callback */
mem_write_handler zc[4]; /* zero crossing callbacks */
int notimer; /* no timer masks */
int mask[4]; /* masked channel flags */
int mode[4]; /* current mode */
int tconst[4]; /* time constant */
int down[4]; /* down counter (clock mode only) */
int extclk[4]; /* current signal from the external clock */
void *timer[4]; /* array of active timers */
int int_state[4]; /* interrupt status (for daisy chain) */
} z80ctc;
static z80ctc ctcs[MAX_CTC];
/* these are the bits of the incoming commands to the CTC */
#define INTERRUPT 0x80
#define INTERRUPT_ON 0x80
#define INTERRUPT_OFF 0x00
#define MODE 0x40
#define MODE_TIMER 0x00
#define MODE_COUNTER 0x40
#define PRESCALER 0x20
#define PRESCALER_256 0x20
#define PRESCALER_16 0x00
#define EDGE 0x10
#define EDGE_FALLING 0x00
#define EDGE_RISING 0x10
#define TRIGGER 0x08
#define TRIGGER_AUTO 0x00
#define TRIGGER_CLOCK 0x08
#define CONSTANT 0x04
#define CONSTANT_LOAD 0x04
#define CONSTANT_NONE 0x00
#define RESET 0x02
#define RESET_CONTINUE 0x00
#define RESET_ACTIVE 0x02
#define CONTROL 0x01
#define CONTROL_VECTOR 0x00
#define CONTROL_WORD 0x01
/* these extra bits help us keep things accurate */
#define WAITING_FOR_TRIG 0x100
static void z80ctc_timercallback (int param);
void z80ctc_init (z80ctc_interface *intf)
{
int i;
memset (ctcs, 0, sizeof (ctcs));
for (i = 0; i < intf->num; i++)
{
ctcs[i].clock = intf->baseclock[i];
ctcs[i].invclock16 = 16.0 / (double)intf->baseclock[i];
ctcs[i].invclock256 = 256.0 / (double)intf->baseclock[i];
ctcs[i].notimer = intf->notimer[i];
ctcs[i].intr = intf->intr[i];
ctcs[i].zc[0] = intf->zc0[i];
ctcs[i].zc[1] = intf->zc1[i];
ctcs[i].zc[2] = intf->zc2[i];
ctcs[i].zc[3] = 0;
z80ctc_reset (i);
}
}
double z80ctc_getperiod (int which, int ch)
{
z80ctc *ctc = ctcs + which;
double clock;
int mode;
/* keep channel within range, and get the current mode */
ch &= 3;
mode = ctc->mode[ch];
/* if reset active */
if( (mode & RESET) == RESET_ACTIVE) return 0;
/* if counter mode */
if( (mode & MODE) == MODE_COUNTER)
{
logerror("CTC %d is CounterMode : Can't calcrate period\n", ch );
return 0;
}
/* compute the period */
clock = ((mode & PRESCALER) == PRESCALER_16) ? ctc->invclock16 : ctc->invclock256;
return clock * (double)ctc->tconst[ch];
}
/* interrupt request callback with daisy-chain circuit */
static void z80ctc_interrupt_check( z80ctc *ctc )
{
int state = 0;
int ch;
for( ch = 3 ; ch >= 0 ; ch-- )
{
/* if IEO disable , same and lower IRQ is masking */
/* ASG: changed this line because this state could have an interrupt pending as well! */
/* if( ctc->int_state[ch] & Z80_INT_IEO ) state = Z80_INT_IEO;*/
if( ctc->int_state[ch] & Z80_INT_IEO ) state = ctc->int_state[ch];
else state |= ctc->int_state[ch];
}
/* change interrupt status */
if (ctc->intr) (*ctc->intr)(state);
}
void z80ctc_reset (int which)
{
z80ctc *ctc = ctcs + which;
int i;
/* set up defaults */
for (i = 0; i < 4; i++)
{
ctc->mode[i] = RESET_ACTIVE;
ctc->tconst[i] = 0x100;
if (ctc->timer[i])
timer_remove (ctc->timer[i]);
ctc->timer[i] = NULL;
ctc->int_state[i] = 0;
}
z80ctc_interrupt_check( ctc );
}
void z80ctc_0_reset (void) { z80ctc_reset (0); }
void z80ctc_1_reset (void) { z80ctc_reset (1); }
void z80ctc_w (int which, int offset, int data)
{
z80ctc *ctc = ctcs + which;
int mode, ch;
/* keep channel within range, and get the current mode */
ch = offset & 3;
mode = ctc->mode[ch];
/* if we're waiting for a time constant, this is it */
if ((mode & CONSTANT) == CONSTANT_LOAD)
{
/* set the time constant (0 -> 0x100) */
ctc->tconst[ch] = data ? data : 0x100;
/* clear the internal mode -- we're no longer waiting */
ctc->mode[ch] &= ~CONSTANT;
/* also clear the reset, since the constant gets it going again */
ctc->mode[ch] &= ~RESET;
/* if we're in timer mode.... */
if ((mode & MODE) == MODE_TIMER)
{
/* if we're triggering on the time constant, reset the down counter now */
if ((mode & TRIGGER) == TRIGGER_AUTO)
{
double clock = ((mode & PRESCALER) == PRESCALER_16) ? ctc->invclock16 : ctc->invclock256;
if (ctc->timer[ch])
timer_remove (ctc->timer[ch]);
if (!(ctc->notimer & (1<<ch)))
ctc->timer[ch] = timer_pulse (clock * (double)ctc->tconst[ch], (which << 2) + ch, z80ctc_timercallback);
}
/* else set the bit indicating that we're waiting for the appropriate trigger */
else
ctc->mode[ch] |= WAITING_FOR_TRIG;
}
/* also set the down counter in case we're clocking externally */
ctc->down[ch] = ctc->tconst[ch];
/* all done here */
return;
}
/* if we're writing the interrupt vector, handle it specially */
#if 0 /* Tatsuyuki Satoh changes */
/* The 'Z80family handbook' wrote, */
/* interrupt vector is able to set for even channel (0 or 2) */
if ((data & CONTROL) == CONTROL_VECTOR && (ch&1) == 0)
#else
if ((data & CONTROL) == CONTROL_VECTOR && ch == 0)
#endif
{
ctc->vector = data & 0xf8;
logerror("CTC Vector = %02x\n", ctc->vector);
return;
}
/* this must be a control word */
if ((data & CONTROL) == CONTROL_WORD)
{
/* set the new mode */
ctc->mode[ch] = data;
logerror("CTC ch.%d mode = %02x\n", ch, data);
/* if we're being reset, clear out any pending timers for this channel */
if ((data & RESET) == RESET_ACTIVE)
{
if (ctc->timer[ch])
timer_remove (ctc->timer[ch]);
ctc->timer[ch] = NULL;
if( ctc->int_state[ch] != 0 )
{
/* clear interrupt service , request */
ctc->int_state[ch] = 0;
z80ctc_interrupt_check( ctc );
}
}
/* all done here */
return;
}
}
WRITE_HANDLER( z80ctc_0_w ) { z80ctc_w (0, offset, data); }
WRITE_HANDLER( z80ctc_1_w ) { z80ctc_w (1, offset, data); }
int z80ctc_r (int which, int ch)
{
z80ctc *ctc = ctcs + which;
int mode;
/* keep channel within range */
ch &= 3;
mode = ctc->mode[ch];
/* if we're in counter mode, just return the count */
if ((mode & MODE) == MODE_COUNTER)
return ctc->down[ch];
/* else compute the down counter value */
else
{
double clock = ((mode & PRESCALER) == PRESCALER_16) ? ctc->invclock16 : ctc->invclock256;
logerror("CTC clock %f\n",1.0/clock);
if (ctc->timer[ch])
return ((int)(timer_timeleft (ctc->timer[ch]) / clock) + 1) & 0xff;
else
return 0;
}
}
READ_HANDLER( z80ctc_0_r ) { return z80ctc_r (0, offset); }
READ_HANDLER( z80ctc_1_r ) { return z80ctc_r (1, offset); }
int z80ctc_interrupt( int which )
{
z80ctc *ctc = ctcs + which;
int ch;
for( ch = 0 ; ch < 4 ; ch++ )
{
if( ctc->int_state[ch] )
{
if( ctc->int_state[ch] == Z80_INT_REQ)
ctc->int_state[ch] = Z80_INT_IEO;
break;
}
}
if( ch > 3 )
{
logerror("CTC entry INT : non IRQ\n");
ch = 0;
}
z80ctc_interrupt_check( ctc );
return ctc->vector + ch * 2;
}
/* when operate RETI , soud be call this function for request pending interrupt */
void z80ctc_reti( int which )
{
z80ctc *ctc = ctcs + which;
int ch;
for( ch = 0 ; ch < 4 ; ch++ )
{
if( ctc->int_state[ch] & Z80_INT_IEO )
{
/* highest served interrupt found */
/* clear interrupt status */
ctc->int_state[ch] &= ~Z80_INT_IEO;
/* search next interrupt */
break;
}
}
/* set next interrupt stattus */
z80ctc_interrupt_check( ctc );
}
static void z80ctc_timercallback (int param)
{
int which = param >> 2;
int ch = param & 3;
z80ctc *ctc = ctcs + which;
/* down counter has reached zero - see if we should interrupt */
if ((ctc->mode[ch] & INTERRUPT) == INTERRUPT_ON)
{
if( !(ctc->int_state[ch] & Z80_INT_REQ) )
{
ctc->int_state[ch] |= Z80_INT_REQ;
z80ctc_interrupt_check( ctc );
}
}
/* generate the clock pulse */
if (ctc->zc[ch])
{
(*ctc->zc[ch])(0,1);
(*ctc->zc[ch])(0,0);
}
/* reset the down counter */
ctc->down[ch] = ctc->tconst[ch];
}
void z80ctc_trg_w (int which, int trg, int offset, int data)
{
z80ctc *ctc = ctcs + which;
int ch = trg & 3;
int mode;
data = data ? 1 : 0;
mode = ctc->mode[ch];
/* see if the trigger value has changed */
if (data != ctc->extclk[ch])
{
ctc->extclk[ch] = data;
/* see if this is the active edge of the trigger */
if (((mode & EDGE) == EDGE_RISING && data) || ((mode & EDGE) == EDGE_FALLING && !data))
{
/* if we're waiting for a trigger, start the timer */
if ((mode & WAITING_FOR_TRIG) && (mode & MODE) == MODE_TIMER)
{
double clock = ((mode & PRESCALER) == PRESCALER_16) ? ctc->invclock16 : ctc->invclock256;
logerror("CTC clock %f\n",1.0/clock);
if (ctc->timer[ch])
timer_remove (ctc->timer[ch]);
if (!(ctc->notimer & (1<<ch)))
ctc->timer[ch] = timer_pulse (clock * (double)ctc->tconst[ch], (which << 2) + ch, z80ctc_timercallback);
}
/* we're no longer waiting */
ctc->mode[ch] &= ~WAITING_FOR_TRIG;
/* if we're clocking externally, decrement the count */
if ((mode & MODE) == MODE_COUNTER)
{
ctc->down[ch]--;
/* if we hit zero, do the same thing as for a timer interrupt */
if (!ctc->down[ch])
z80ctc_timercallback ((which << 2) + ch);
}
}
}
}
WRITE_HANDLER( z80ctc_0_trg0_w ) { z80ctc_trg_w (0, 0, offset, data); }
WRITE_HANDLER( z80ctc_0_trg1_w ) { z80ctc_trg_w (0, 1, offset, data); }
WRITE_HANDLER( z80ctc_0_trg2_w ) { z80ctc_trg_w (0, 2, offset, data); }
WRITE_HANDLER( z80ctc_0_trg3_w ) { z80ctc_trg_w (0, 3, offset, data); }
WRITE_HANDLER( z80ctc_1_trg0_w ) { z80ctc_trg_w (1, 0, offset, data); }
WRITE_HANDLER( z80ctc_1_trg1_w ) { z80ctc_trg_w (1, 1, offset, data); }
WRITE_HANDLER( z80ctc_1_trg2_w ) { z80ctc_trg_w (1, 2, offset, data); }
WRITE_HANDLER( z80ctc_1_trg3_w ) { z80ctc_trg_w (1, 3, offset, data); }
/*---------------------- Z80 PIO ---------------------------------*/
/* starforce emurate Z80PIO subset */
/* ch.A mode 1 input handshake mode from sound command */
/* ch.b not use */
#define PIO_MODE0 0x00 /* output mode */
#define PIO_MODE1 0x01 /* input mode */
#define PIO_MODE2 0x02 /* i/o mode */
#define PIO_MODE3 0x03 /* bit mode */
/* pio controll port operation (bit 0-3) */
#define PIO_OP_MODE 0x0f /* mode select */
#define PIO_OP_INTC 0x07 /* interrupt controll */
#define PIO_OP_INTE 0x03 /* interrupt enable */
#define PIO_OP_INTE 0x03 /* interrupt enable */
/* pio interrupt controll nit */
#define PIO_INT_ENABLE 0x80 /* ENABLE : 0=disable , 1=enable */
#define PIO_INT_AND 0x40 /* LOGIC : 0=OR , 1=AND */
#define PIO_INT_HIGH 0x20 /* LEVEL : 0=low , 1=high */
#define PIO_INT_MASK 0x10 /* MASK : 0=off , 1=on */
typedef struct
{
int vector[2]; /* interrupt vector */
void (*intr)(int which); /* interrupt callbacks */
void (*rdyr[2])(int data); /* RDY active callback */
int mode[2]; /* mode 00=in,01=out,02=i/o,03=bit*/
int enable[2]; /* interrupt enable */
int mask[2]; /* mask folowers */
int dir[2]; /* direction (bit mode) */
int rdy[2]; /* ready pin level */
int in[2]; /* input port data */
int out[2]; /* output port */
int int_state[2]; /* interrupt status (daisy chain) */
} z80pio;
static z80pio pios[MAX_PIO];
/* initialize pio emurator */
void z80pio_init (z80pio_interface *intf)
{
int i;
memset (pios, 0, sizeof (pios));
for (i = 0; i < intf->num; i++)
{
pios[i].intr = intf->intr[i];
pios[i].rdyr[0] = intf->rdyA[i];
pios[i].rdyr[1] = intf->rdyB[i];
z80pio_reset (i);
}
}
static void z80pio_interrupt_check( z80pio *pio )
{
int state;
if( pio->int_state[1] & Z80_INT_IEO ) state = Z80_INT_IEO;
else state = pio->int_state[1];
if( pio->int_state[0] & Z80_INT_IEO ) state = Z80_INT_IEO;
else state |= pio->int_state[0];
/* change daisy chain status */
if (pio->intr) (*pio->intr)(state);
}
static void z80pio_check_irq( z80pio *pio , int ch )
{
int irq = 0;
int data;
int old_state;
if( pio->enable[ch] & PIO_INT_ENABLE )
{
if( pio->mode[ch] == PIO_MODE3 )
{
data = pio->in[ch] & pio->dir[ch]; /* input data only */
data &= ~pio->mask[ch]; /* mask follow */
if( !(pio->enable[ch]&PIO_INT_HIGH) )/* active level */
data ^= pio->mask[ch]; /* active low */
if( pio->enable[ch]&PIO_INT_AND ) /* logic */
{ if( data == pio->mask[ch] ) irq = 1; }
else { if( data == 0 ) irq = 1; }
/* if portB , portA mode 2 check */
if( ch && (pio->mode[0]==PIO_MODE2) )
{
if( pio->rdy[ch] == 0 ) irq = 1;
}
}
else if( pio->rdy[ch] == 0 ) irq = 1;
}
old_state = pio->int_state[ch];
if( irq ) pio->int_state[ch] |= Z80_INT_REQ;
else pio->int_state[ch] &= ~Z80_INT_REQ;
if( old_state != pio->int_state[ch] )
z80pio_interrupt_check( pio );
}
void z80pio_reset (int which)
{
z80pio *pio = pios + which;
int i;
for( i = 0 ; i <= 1 ; i++){
pio->mask[i] = 0xff; /* mask all on */
pio->enable[i] = 0x00; /* disable */
pio->mode[i] = 0x01; /* mode input */
pio->dir[i] = 0x01; /* dir input */
pio->rdy[i] = 0x00; /* RDY = low */
pio->out[i] = 0x00; /* outdata = 0 */
pio->int_state[i] = 0;
}
z80pio_interrupt_check( pio );
}
/* pio data register write */
void z80pio_d_w( int which , int ch , int data )
{
z80pio *pio = pios + which;
if( ch ) ch = 1;
pio->out[ch] = data; /* latch out data */
switch( pio->mode[ch] ){
case PIO_MODE0: /* mode 0 output */
case PIO_MODE2: /* mode 2 i/o */
pio->rdy[ch] = 1; /* ready = H */
z80pio_check_irq( pio , ch );
return;
case PIO_MODE1: /* mode 1 intput */
case PIO_MODE3: /* mode 0 bit */
return;
default:
logerror("PIO-%c data write,bad mode\n",'A'+ch );
}
}
/* pio controll register write */
void z80pio_c_w( int which , int ch , int data )
{
z80pio *pio = pios + which;
if( ch ) ch = 1;
/* load direction phase ? */
if( pio->mode[ch] == 0x13 ){
pio->dir[ch] = data;
pio->mode[ch] = 0x03;
return;
}
/* load mask folows phase ? */
if( pio->enable[ch] & PIO_INT_MASK ){ /* load mask folows */
pio->mask[ch] = data;
pio->enable[ch] &= ~PIO_INT_MASK;
logerror("PIO-%c interrupt mask %02x\n",'A'+ch,data );
return;
}
switch( data & 0x0f ){
case PIO_OP_MODE: /* mode select 0=out,1=in,2=i/o,3=bit */
pio->mode[ch] = (data >> 6 );
if( pio->mode[ch] == 0x03 ) pio->mode[ch] = 0x13;
logerror("PIO-%c Mode %x\n",'A'+ch,pio->mode[ch] );
break;
case PIO_OP_INTC: /* interrupt control */
pio->enable[ch] = data & 0xf0;
pio->mask[ch] = 0x00;
/* when interrupt enable , set vector request flag */
logerror("PIO-%c Controll %02x\n",'A'+ch,data );
break;
case PIO_OP_INTE: /* interrupt enable controll */
pio->enable[ch] &= ~PIO_INT_ENABLE;
pio->enable[ch] |= (data & PIO_INT_ENABLE);
logerror("PIO-%c enable %02x\n",'A'+ch,data&0x80 );
break;
default:
if( !(data&1) )
{
pio->vector[ch] = data;
logerror("PIO-%c vector %02x\n",'A'+ch,data);
}
else logerror("PIO-%c illegal command %02x\n",'A'+ch,data );
}
/* interrupt check */
z80pio_check_irq( pio , ch );
}
/* pio controll register read */
int z80pio_c_r( int which , int ch )
{
if( ch ) ch = 1;
logerror("PIO-%c controll read\n",'A'+ch );
return 0;
}
/* pio data register read */
int z80pio_d_r( int which , int ch )
{
z80pio *pio = pios + which;
if( ch ) ch = 1;
switch( pio->mode[ch] ){
case PIO_MODE0: /* mode 0 output */
return pio->out[ch];
case PIO_MODE1: /* mode 1 intput */
pio->rdy[ch] = 1; /* ready = H */
z80pio_check_irq( pio , ch );
return pio->in[ch];
case PIO_MODE2: /* mode 2 i/o */
if( ch ) logerror("PIO-B mode 2 \n");
pio->rdy[1] = 1; /* brdy = H */
z80pio_check_irq( pio , ch );
return pio->in[ch];
case PIO_MODE3: /* mode 3 bit */
return (pio->in[ch]&pio->dir[ch])|(pio->out[ch]&~pio->dir[ch]);
}
logerror("PIO-%c data read,bad mode\n",'A'+ch );
return 0;
}
int z80pio_interrupt( int which )
{
z80pio *pio = pios + which;
int ch = 0;
/* port A */
if( pio->int_state[0] == Z80_INT_REQ )
{
pio->int_state[0] |= Z80_INT_IEO;
} if( pio->int_state[0] == 0 )
{
/* port B */
ch = 1;
if( pio->int_state[1] == Z80_INT_REQ )
{
pio->int_state[1] |= Z80_INT_IEO;
}
else
{
logerror("PIO entry INT : non IRQ\n");
ch = 0;
}
}
z80pio_interrupt_check( pio );
return pio->vector[ch];
}
void z80pio_reti( int which )
{
z80pio *pio = pios + which;
if( pio->int_state[0] & Z80_INT_IEO )
{
pio->int_state[0] &= ~Z80_INT_IEO;
} else if( pio->int_state[1] & Z80_INT_IEO )
{
pio->int_state[1] &= ~Z80_INT_IEO;
}
/* set next interrupt stattus */
z80pio_interrupt_check( pio );
}
/* z80pio port write */
void z80pio_p_w( int which , int ch , int data )
{
z80pio *pio = pios + which;
if( ch ) ch = 1;
pio->in[ch] = data;
switch( pio->mode[ch] ){
case PIO_MODE0:
logerror("PIO-%c OUTPUT mode and data write\n",'A'+ch );
break;
case PIO_MODE2: /* only port A */
ch = 1; /* handshake and IRQ is use portB */
case PIO_MODE1:
pio->rdy[ch] = 0;
z80pio_check_irq( pio , ch );
break;
case PIO_MODE3:
/* irq check */
z80pio_check_irq( pio , ch );
break;
}
}
/* z80pio port read */
int z80pio_p_r( int which , int ch )
{
z80pio *pio = pios + which;
if( ch ) ch = 1;
switch( pio->mode[ch] ){
case PIO_MODE2: /* port A only */
case PIO_MODE0:
pio->rdy[ch] = 0;
z80pio_check_irq( pio , ch );
break;
case PIO_MODE1:
logerror("PIO-%c INPUT mode and data read\n",'A'+ch );
break;
case PIO_MODE3:
/* input bits , output bits */
return (pio->in[ch]&pio->dir[ch])|(pio->out[ch]&~pio->dir[ch]);
}
return pio->out[ch];
}
/* for mame interface */
void z80pio_0_reset (void) { z80pio_reset (0); }
WRITE_HANDLER( z80pio_0_w )
{
if(offset&1) z80pio_c_w(0,(offset/2)&1,data);
else z80pio_d_w(0,(offset/2)&1,data);
}
READ_HANDLER( z80pio_0_r )
{
return (offset&1) ? z80pio_c_r(0,(offset/2)&1) : z80pio_d_r(0,(offset/2)&1);
}
WRITE_HANDLER( z80pioA_0_p_w ) { z80pio_p_w(0,0,data); }
WRITE_HANDLER( z80pioB_0_p_w ) { z80pio_p_w(0,1,data); }
READ_HANDLER( z80pioA_0_p_r ) { return z80pio_p_r(0,0); }
READ_HANDLER( z80pioB_0_p_r ) { return z80pio_p_r(0,1); }